﻿using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace DimensionsHelper.LanguageService.Syntax;

public class MetadataField : MetadataLabeledObject
{
    private MetadataAxis? _axis;
    private MetadataCategories? _categories;
    private MetadataClass? _class;
    private MetadataCodes? _codes;
    private SyntaxToken? _columnKeywordToken;

    // Db fields

    private MetadataDbProperties? _dbProperties;
    private MetadataDefaultAnswer? _defaultAnswer;

    private SyntaxToken? _expandKeywordToken;

    private MetadataExpression? _expression;
    private SyntaxToken? _gridKeywordToken;
    private MetadataHelperFields? _helperFields;
    private MetadataInitialAnswer? _initialAnswer;
    private SyntaxToken? _noCaseDataKeywordToken;

    private SyntaxList<Identifier>? _pageItems;
    private MetadataPrecision? _precision;
    private SyntaxToken? _rowKeywordToken;
    private MetadataScale? _scale;
    private SyntaxToken? _unversionedKeywordToken;
    private MetadataUsageType? _usageType;
    private MetadataValidation? _validation;
    private MetadataFieldValueRangeList? _valueRanges;
    private SyntaxToken? _versionedKeywordToken;

    internal MetadataField(Identifier name, SyntaxToken declareKeywordToken, SyntaxNode? parent) :
        base(SyntaxKinds.MetadataField,
            declareKeywordToken,
            name,
            parent)
    {
    }


    public MetadataFieldValueRangeList? ValueRanges => _valueRanges;

    public MetadataCategories? Categories => _categories;

    public MetadataClass? Class => _class;

    public SyntaxToken? NoCaseDataKeywordToken => _noCaseDataKeywordToken;

    public SyntaxToken? UnVersionedKeywordToken => _unversionedKeywordToken;

    public SyntaxToken? VersionedKeywordToken => _versionedKeywordToken;

    public MetadataExpression? Expression => _expression;

    public MetadataDefaultAnswer? DefaultAnswer => _defaultAnswer;

    public MetadataInitialAnswer? InitialAnswer => _initialAnswer;

    public MetadataAxis? Axis => _axis;

    public MetadataPrecision? Precision => _precision;

    public MetadataScale? Scale => _scale;

    public MetadataValidation? Validation => _validation;

    public MetadataCodes? Codes => _codes;

    public MetadataHelperFields? HelperFields => _helperFields;

    public MetadataUsageType? UsageType => _usageType;

    public SyntaxToken? RowKeywordToken => _rowKeywordToken;

    public SyntaxToken? ColumnKeywordToken => _columnKeywordToken;

    public SyntaxToken? ExpandKeywordToken => _expandKeywordToken;

    public SyntaxToken? GridKeywordToken => _gridKeywordToken;

    public MetadataDbProperties? DbProperties => _dbProperties;

    public bool IsElementList => DeclareKeywordToken.IsNode(SyntaxKinds.DefineKeywordToken);

    public bool IsPage => DeclareKeywordToken.IsNode(SyntaxKinds.PageKeywordToken);

    public bool IsInfo => DeclareKeywordToken.IsNode(SyntaxKinds.InfoKeywordToken);

    public bool IsLoop => DeclareKeywordToken.IsNode(SyntaxKinds.LoopKeywordToken);

    public bool IsGrid => DeclareKeywordToken.IsNode(SyntaxKinds.GridKeywordToken);

    public bool IsBlock => DeclareKeywordToken.IsNode(SyntaxKinds.BlockKeywordToken);

    public bool IsCompound => DeclareKeywordToken.IsNode(SyntaxKinds.CompoundKeywordToken);

    public bool IsCategorical => DeclareKeywordToken.IsNode(SyntaxKinds.CategoricalKeywordToken);

    public bool IsLong => DeclareKeywordToken.IsNode(SyntaxKinds.LongKeywordToken);

    public bool IsDouble => DeclareKeywordToken.IsNode(SyntaxKinds.DoubleKeywordToken);

    public bool IsDate => DeclareKeywordToken.IsNode(SyntaxKinds.DateKeywordToken);

    public bool IsText => DeclareKeywordToken.IsNode(SyntaxKinds.TextKeywordToken);

    public bool IsBoolean => DeclareKeywordToken.IsNode(SyntaxKinds.BooleanKeywordToken);


    [MemberNotNullWhen(true, nameof(DbProperties))]
    public bool IsDbField => DbProperties != null;


    /// <summary>
    ///     获取page字段中的字段名列表。
    /// </summary>
    public ImmutableArray<Identifier> GetPageItems()
    {
        return _pageItems?.ToImmutableArray() ?? ImmutableArray<Identifier>.Empty;
    }


    /// <summary>
    ///     向页面字段下添加引用的字段名，需要<see cref="IsPage" />是<see langword="true" />
    /// </summary>
    /// <param name="pageItem">引用的字段名</param>
    public void Update(Identifier pageItem)
    {
        Debug.Assert(IsPage);
        if (_pageItems == null)
        {
            _pageItems = new SyntaxList<Identifier>();
            _pageItems.SetUri(Uri);
        }

        _pageItems.Add(pageItem);
    }


    public void Update(MetadataFieldValueRangeList valueRanges)
    {
        SetChildNullableField(ref _valueRanges, valueRanges);
    }


    public void Update(MetadataCategories categories)
    {
        SetChildNullableField(ref _categories, categories);
    }


    public void Update(MetadataClass metadataClass)
    {
        SetChildNullableField(ref _class, metadataClass);
    }


    public void Update(SyntaxToken? noCaseDataKeywordToken, SyntaxToken? unversionedKeywordToken,
        SyntaxToken? versionedKeywordToken)
    {
        Debug.Assert(noCaseDataKeywordToken == null ||
                     noCaseDataKeywordToken.IsNode(SyntaxKinds.NoCaseDataKeywordToken));
        Debug.Assert(unversionedKeywordToken == null ||
                     unversionedKeywordToken.IsNode(SyntaxKinds.UnversionedKeywordToken));
        Debug.Assert(versionedKeywordToken == null ||
                     versionedKeywordToken.IsNode(SyntaxKinds.VersionedKeywordToken));
        SetChildNullableField(ref _noCaseDataKeywordToken, SyntaxToken.IsNullOrEmpty, noCaseDataKeywordToken);
        SetChildNullableField(ref _unversionedKeywordToken, SyntaxToken.IsNullOrEmpty, unversionedKeywordToken);
        SetChildNullableField(ref _versionedKeywordToken, SyntaxToken.IsNullOrEmpty, versionedKeywordToken);
    }

    public void Update(MetadataExpression expression)
    {
        SetChildNullableField(ref _expression, expression);
    }

    public void Update(MetadataDefaultAnswer defaultAnswer)
    {
        SetChildNullableField(ref _defaultAnswer, defaultAnswer);
    }

    public void Update(MetadataInitialAnswer initialAnswer)
    {
        SetChildNullableField(ref _initialAnswer, initialAnswer);
    }

    public void Update(MetadataAxis axis)
    {
        SetChildNullableField(ref _axis, axis);
    }

    public void Update(MetadataPrecision precision)
    {
        SetChildNullableField(ref _precision, precision);
    }

    public void Update(MetadataScale scale)
    {
        SetChildNullableField(ref _scale, scale);
    }

    public void Update(MetadataValidation validation)
    {
        SetChildNullableField(ref _validation, validation);
    }

    public void Update(MetadataCodes codes)
    {
        SetChildNullableField(ref _codes, codes);
    }

    public void Update(MetadataHelperFields helperFields)
    {
        SetChildNullableField(ref _helperFields, helperFields);
    }

    public void Update(MetadataUsageType usageType)
    {
        SetChildNullableField(ref _usageType, usageType);
    }


    public void Update(SyntaxToken? rowKeywordToken, SyntaxToken? columnKeywordToken,
        SyntaxToken? expandKeywordToken, SyntaxToken? gridKeywordToken)
    {
        Debug.Assert(rowKeywordToken == null || rowKeywordToken.IsNode(SyntaxKinds.RowKeywordToken));
        Debug.Assert(columnKeywordToken == null || columnKeywordToken.IsNode(SyntaxKinds.ColumnKeywordToken));
        Debug.Assert(expandKeywordToken == null || expandKeywordToken.IsNode(SyntaxKinds.ExpandKeywordToken));
        Debug.Assert(gridKeywordToken == null || gridKeywordToken.IsNode(SyntaxKinds.GridKeywordToken));
        SetChildNullableField(ref _rowKeywordToken, SyntaxToken.IsNullOrEmpty, rowKeywordToken);
        SetChildNullableField(ref _columnKeywordToken, SyntaxToken.IsNullOrEmpty, columnKeywordToken);
        SetChildNullableField(ref _expandKeywordToken, SyntaxToken.IsNullOrEmpty, expandKeywordToken);
        SetChildNullableField(ref _gridKeywordToken, SyntaxToken.IsNullOrEmpty, gridKeywordToken);
    }

    public void Update(MetadataDbProperties dbProperties)
    {
        SetChildNullableField(ref _dbProperties, dbProperties);
    }


    public override void AcceptVisitor<T>(SyntaxNodeVisitor<T> visitor)
    {
        base.AcceptVisitor(visitor);
        Accept(visitor,
            ValueRanges,
            Categories,
            Class,
            NoCaseDataKeywordToken,
            VersionedKeywordToken,
            UnVersionedKeywordToken,
            Expression,
            DefaultAnswer,
            InitialAnswer,
            Axis,
            Precision,
            Scale,
            Validation,
            Codes,
            HelperFields,
            UsageType,
            RowKeywordToken,
            ColumnKeywordToken,
            ExpandKeywordToken,
            GridKeywordToken);
        AcceptList(visitor, _pageItems);
    }
}


public static partial class NodeFactory
{
    public static MetadataField CreateMetadataField(SyntaxNode? parent, Identifier identifierName,
        SyntaxToken declareKeywordToken, MetadataFieldValueRangeList? valueRanges)
    {
        MetadataField field = new(identifierName, declareKeywordToken, parent);

        if (valueRanges != null)
        {
            field.Update(valueRanges);
        }

        return field;
    }
}